package transport;

import network.*;

import java.util.*;

public class SlidingWindow {

	String destName;
	static long timeoutAck = 10000;
	static int SNDSize = 5;
	HashMap<Integer, Timer> HashTimeOut;
	HashMap<Integer, String> HashSND = new HashMap<Integer, String>();
	int indiceMap = 1;
	int lastMessageSent = 0;
	int lastAckReceive = 0;
	int sendMode = 0;
	int storedMessages = 0;
	
	// friend:myName:HELLOSW:SNDSize
	// friend:myName:MESSAGESW:number:message-content:
	// myName:friend:ACKSW:number:
	// friend:myName:ENDACKSW:

	SlidingWindow(String destName, String msg) {
		this.destName = destName;
		HashTimeOut = new HashMap<Integer, Timer>();
		sendOpenAck();
		HashSND.put(indiceMap, msg);
		indiceMap++;
	}

	synchronized void sendOpenAck() {
		String msg = openAckMessage();
		SWAckTask task = new SWAckTask(msg);
		Timer timeout = new Timer();
		timeout.schedule(task, 0, timeoutAck);
		HashTimeOut.put(0, timeout);
	}

	String openAckMessage() {
		StringBuffer buf = new StringBuffer();
		buf.append(destName).append(":").append(TransportLayer.myID).append(
				":HELLOSW:").append(SNDSize + ":");
		return buf.toString();
	}

	String endAckMessage() {
		StringBuffer buf = new StringBuffer();
		buf.append(destName).append(":").append(TransportLayer.myID).append(
				":ENDACKSW:");
		return buf.toString();
	}

	synchronized void updateHashSND(String msg) {
		HashSND.put(indiceMap, msg);
		indiceMap++;
	}

	StringBuffer messageMSG(int num) {
		StringBuffer buf = new StringBuffer();
		buf.append(destName).append(":").append(TransportLayer.myID)
				.append(":").append("MESSAGESW").append(":").append(
						Integer.toString(num)).append(":");
		return buf;
	}

	synchronized void sendMsg() {
		if (HashSND.isEmpty()&&sendMode==2) {
			TransportLayer.viderSWsend(destName);
			TransportLayer.viderSWreceive(destName);
			return;
		}
		// on envoie pas de message tant que la fen�tre de messages est pleine
		//if ((SND[SNDSize - 1] == 0)&&(sendMode==1)) {
		if (sendMode==1) {	
			int n = lastMessageSent + 1;
			if (HashSND.containsKey(n)&&(n<=lastAckReceive+SNDSize)) {
				String msg = HashSND.get(n);
				//UpdateSND(n);
				StringBuffer buff = new StringBuffer();
				buff = messageMSG(n);
				buff.append(msg);
				String message = buff.toString();
				sendMessage(message, n);
			}
			else if (!HashSND.containsKey(n)) {
				NetworkLayer.send(endAckMessage());
				sendMode = 2;
			}
		}
	}

	synchronized void sendMessage(String message, int n) {
		NetworkLayer.send(message);
		lastMessageSent++;
		SWAckTask task = new SWAckTask(message);
		Timer timeout = new Timer();
		timeout.schedule(task, timeoutAck, timeoutAck);
		HashTimeOut.put(n, timeout);
		sendMsg();
	}

	synchronized void processAck(String message) {
		String[] splitMessage = message.split(":");
		int messageId = Integer.parseInt(splitMessage[3]);
		if (messageId == 0) {
			HashTimeOut.get(0).cancel();
			sendMode = 1;
			sendMsg();
		} else if (messageId > lastAckReceive) {
			int k = lastAckReceive;
			for (int i = k; i < messageId; i++) {
				HashSND.remove(i + 1);
				HashTimeOut.get(i+1).cancel();
				lastAckReceive++;
			}
			sendMsg();
		}
	}
}